7  NumPy 矩阵处理

7.1 引言矩阵在金融中的核心地位

矩阵(Matrix)是金融数学的核心工具。从投资组合优化到风险因子模型,从期权定价到主成分分析,矩阵运算无处不在。NumPy提供了完整的线性代数工具,使得复杂的金融计算变得简洁高效。

理论背景:线性代数与金融

线性代数在金融中的应用源于多维数据结构: - 投资组合: N只资产的收益率可以表示为N维向量 - 协方差矩阵: N×N矩阵描述资产间的风险关系 - 因子模型: 收益率 = 因子载荷 × 因子收益 + 特质收益

理解矩阵运算对掌握现代投资组合理论(MPT)、资本资产定价模型(CAPM)等金融理论至关重要。

7.2 矩阵基础

7.2.1 矩阵的创建

列表 7.1
# =============================================================================
# 题目:创建NumPy矩阵的不同方法
# =============================================================================
# 本节演示多种创建矩阵的方法,包括从二维数组创建、特殊矩阵创建等
# 在金融中,矩阵常用于表示多资产收益率、协方差矩阵等

# ==================== 导入NumPy库 ====================
import numpy as np  # 导入NumPy库,用于矩阵运算和线性代数操作

# ==================== 方法1:使用二维数组创建矩阵(推荐) ====================
matrix1 = np.array([  # 使用array函数创建二维数组作为矩阵
    [1, 2, 3],  # 第一行数据,包含3个元素
    [4, 5, 6],  # 第二行数据,包含3个元素
    [7, 8, 9]   # 第三行数据,包含3个元素
])  # 创建一个3×3的矩阵
print("3×3矩阵:")
print(matrix1)  # 打印矩阵内容,显示为3行3列的二维数组

# ==================== 方法2:使用特殊矩阵函数创建 ====================
identity = np.eye(3)  # eye函数创建单位矩阵(对角线为1,其余为0),参数3表示3×3
print("\n单位矩阵:")
print(identity)  # 打印单位矩阵,在金融中常作为初始权重或转换基准

zeros = np.zeros((2, 3))  # zeros函数创建全0矩阵,参数(2,3)表示2行3列
print("\n零矩阵:")
print(zeros)  # 打印零矩阵,常用于初始化累加变量或占位

ones = np.ones((2, 4))  # ones函数创建全1矩阵,参数(2,4)表示2行4列
print("\n全1矩阵:")
print(ones)  # 打印全1矩阵,可用于权重初始化或广播运算

# ==================== 方法3:创建对角矩阵 ====================
diag = np.diag([1, 2, 3])  # diag函数创建对角矩阵,参数[1,2,3]为对角线元素
print("\n对角矩阵:")
print(diag)  # 打印对角矩阵,在金融中可用于表示波动率或因子权重

补充说明:矩阵 vs 数组

特性 np.matrix np.ndarray
运算符行为 *表示矩阵乘法 *表示元素乘法
维度 始终2维 可以任意维
状态 已弃用(1.20+) 推荐使用
建议 不要使用 始终使用

最佳实践: 始终使用np.ndarray表示矩阵,使用@运算符进行矩阵乘法。

7.3 矩阵乘法

7.3.1 点积(Dot Product)

数学定义: 两个向量\(\mathbf{a} = [a_1, a_2, ..., a_n]\)\(\mathbf{b} = [b_1, b_2, ..., b_n]\)的点积为:

\[ \mathbf{a} \cdot \mathbf{b} = \sum_{i=1}^{n} a_i b_i = a_1b_1 + a_2b_2 + \cdots + a_nb_n \]

金融含义: 点积常用于计算加权平均,如投资组合收益率:

\[ R_p = \sum_{i=1}^{n} w_i R_i = \mathbf{w} \cdot \mathbf{R} \]

其中\(\mathbf{w}\)是权重向量,\(\mathbf{R}\)是收益率向量。

列表 7.2
# ⚠️ 平台原始代码 - 请原样输入至教学平台(注释除外),平台才会判定答案正确
import numpy as np  # 导入NumPy数值计算库
#使用列表定义5月26日收益率数据 return_0526、权重数据 weight。
return_0526=[0.0172,0.0243,-0.0029,0.0021]
weight=[0.15,0.20,0.25,0.4]  # 定义列表weight
#计算投资组合日收益率
return_daily=np.dot(return_0526,weight).round(3)
print(f'投资组合5月26日收益率为:{return_daily}')  # 输出投资组合5月26日收益率为

代码深度解析:

  1. 点积的几何意义: \[ \mathbf{a} \cdot \mathbf{b} = \|\mathbf{a}\| \|\mathbf{b}\| \cos\theta \] 其中\(\theta\)是两个向量的夹角。

  2. 金融应用:

    • 投资组合收益: \(\sum w_i R_i\)
    • 因子收益: \(\sum \beta_i F_i\)
    • 内积作为相似度: 衡量两个投资组合的相似程度

7.3.2 矩阵乘法

数学定义: 矩阵\(\mathbf{A}\)(\(m \times n\))与矩阵\(\mathbf{B}\)(\(n \times p\))的乘积\(\mathbf{C}\)(\(m \times p\))为:

\[ c_{ij} = \sum_{k=1}^{n} a_{ik} b_{kj} \]

重要性质: - 不满足交换律: \(\mathbf{AB} \neq \mathbf{BA}\) - 满足结合律: \((\mathbf{AB})\mathbf{C} = \mathbf{A}(\mathbf{BC})\) - 维度要求: \(\mathbf{A}\)的列数必须等于\(\mathbf{B}\)的行数

列表 7.3
# =============================================================================
# 题目:NumPy矩阵乘法的多种方法
# =============================================================================
# 演示三种矩阵乘法方法:@运算符、np.matmul()函数、np.dot()函数
# 矩阵乘法要求:左矩阵的列数 = 右矩阵的行数

import numpy as np  # 导入NumPy库

# ==================== 定义两个矩阵用于乘法演示 ====================
A = np.array([  # 定义第一个矩阵A,2行3列
    [1, 2, 3],  # A的第1行
    [4, 5, 6]   # A的第2行
])  # A是一个2×3矩阵

B = np.array([  # 定义第二个矩阵B,3行2列
    [7, 8],     # B的第1行
    [9, 10],    # B的第2行
    [11, 12]    # B的第3行
])  # B是一个3×2矩阵
# 注意:A的列数(3)等于B的行数(3),可以相乘,结果将是2×2矩阵

# ==================== 方法1:@运算符(推荐,Python 3.5+) ====================
C1 = A @ B  # @运算符执行矩阵乘法,结果为2×2矩阵
print("方法1 - @运算符:")
print(C1)  # 打印乘积矩阵

# ==================== 方法2:np.matmul()函数 ====================
C2 = np.matmul(A, B)  # matmul函数执行矩阵乘法,与@运算符等价
print("\n方法2 - matmul函数:")
print(C2)  # 打印乘积矩阵,结果与方法1相同

# ==================== 方法3:np.dot()函数(也支持矩阵乘法) ====================
C3 = np.dot(A, B)  # dot函数也可用于矩阵乘法(虽然主要用于点积)
print("\n方法3 - dot函数:")
print(C3)  # 打印乘积矩阵,三种方法结果完全一致

# ==================== 验证三种方法的结果一致性 ====================
print(f"\n结果一致: {np.array_equal(C1, C2) and np.array_equal(C2, C3)}")  # array_equal逐元素比较,确认结果相同

金融应用:多期投资组合收益

列表 7.4
# =============================================================================
# 题目:矩阵运算计算多期投资组合收益
# =============================================================================
# 场景:3只股票在4个交易日的收益率矩阵,计算投资组合每天的收益率
# 数学原理:组合收益率 = 权重向量 × 收益率矩阵

import numpy as np  # 导入NumPy库

# ==================== 数据准备:收益率矩阵 ====================
returns = np.array([  # 创建3×4的收益率矩阵
    [0.01, 0.02, -0.01, 0.03],  # 股票A的4日收益率
    [0.02, 0.01, 0.03, -0.01],  # 股票B的4日收益率
    [-0.01, 0.03, 0.02, 0.04]   # 股票C的4日收益率
])  # returns是3×4矩阵,每行代表一只股票,每列代表一个交易日

# ==================== 投资组合权重向量 ====================
weights = np.array([0.3, 0.5, 0.2])  # 定义投资组合权重,3只股票权重分别为30%、50%、20%
# 权重总和为1,表示满仓投资

# ==================== 计算组合每日收益率 ====================
portfolio_returns = weights @ returns  # 矩阵乘法:权重向量(1×3) × 收益率矩阵(3×4) = 组合收益向量(1×4)
print(f"投资组合每日收益率: {portfolio_returns}")  # 打印4个交易日的组合收益率

# ==================== 计算期间累计收益率 ====================
cumulative_return = np.prod(1 + portfolio_returns) - 1  # prod计算连乘,累计收益率公式:(1+r1)×(1+r2)×...×(1+rn) - 1
print(f"期间累计收益率: {cumulative_return:.4%}")  # 打印累计收益率,:.4%格式化为百分比

7.4 协方差矩阵与相关系数矩阵

协方差矩阵(Covariance Matrix)是金融风险管理的核心工具。

数学定义: 对于随机向量\(\mathbf{X} = [X_1, X_2, ..., X_n]^T\),协方差矩阵\(\mathbf{\Sigma}\)为:

\[ \mathbf{\Sigma}_{ij} = \text{Cov}(X_i, X_j) = E[(X_i - \mu_i)(X_j - \mu_j)] \]

性质: 1. 对称性: \(\mathbf{\Sigma}_{ij} = \mathbf{\Sigma}_{ji}\) 2. 半正定性: \(\mathbf{a}^T\mathbf{\Sigma}\mathbf{a} \geq 0\)对所有\(\mathbf{a}\) 3. 对角线元素: \(\mathbf{\Sigma}_{ii} = \text{Var}(X_i)\)

列表 7.5
# =============================================================================
# 题目:计算协方差矩阵与相关系数矩阵
# =============================================================================
# 协方差矩阵衡量多资产之间的风险相关性,是投资组合优化的核心输入
# 相关系数矩阵是标准化的协方差矩阵,取值范围[-1, 1]

import numpy as np  # 导入NumPy库

# ==================== 数据准备:模拟3只股票的日收益率 ====================
np.random.seed(42)  # 设置随机种子为42,确保结果可重现
returns = np.random.randn(100, 3) * 0.02  # 生成100天×3只股票的随机收益率,标准差2%
# randn生成标准正态分布,乘以0.02调整波动率

# ==================== 计算协方差矩阵 ====================
cov_matrix = np.cov(returns, rowvar=False)  # cov计算协方差矩阵,rowvar=False表示每列是一个变量
print("协方差矩阵:")
print(cov_matrix)  # 打印3×3协方差矩阵,对角线是各股票方差,非对角线是协方差

# ==================== 计算相关系数矩阵 ====================
corr_matrix = np.corrcoef(returns, rowvar=False)  # corrcoef计算相关系数矩阵
print("\n相关系数矩阵:")
print(corr_matrix)  # 打印相关系数矩阵,值域为[-1, 1]

# ==================== 从协方差矩阵推导相关系数矩阵 ====================
# 公式: Corr(X,Y) = Cov(X,Y) / (Std(X) * Std(Y))
std_devs = np.sqrt(np.diag(cov_matrix))  # sqrt对角线元素开方得到标准差向量
corr_from_cov = cov_matrix / np.outer(std_devs, std_devs)  # outer计算外积,广播得到分母矩阵
print("\n从协方差计算的相关系数矩阵:")
print(corr_from_cov)  # 打印推导的相关系数矩阵

# ==================== 验证两种方法结果一致性 ====================
print(f"\n两种方法一致: {np.allclose(corr_matrix, corr_from_cov)}")  # allclose在容差范围内比较浮点数

金融应用:投资组合方差

马科维茨投资组合理论的核心公式:

\[ \sigma_p^2 = \mathbf{w}^T \mathbf{\Sigma} \mathbf{w} = \sum_{i=1}^{n}\sum_{j=1}^{n} w_i w_j \sigma_{ij} \]

列表 7.6
# =============================================================================
# 题目:使用矩阵运算计算投资组合方差
# =============================================================================
# 投资组合方差 = 权重向量 × 协方差矩阵 × 权重向量转置
# 这是马科维茨现代投资组合理论(MPT)的核心公式

import numpy as np  # 导入NumPy库

# ==================== 数据准备:3只股票的协方差矩阵 ====================
cov_matrix = np.array([  # 定义3×3协方差矩阵
    [0.0100, 0.0018, 0.0011],  # 股票A:方差0.01,与B协方差0.0018,与C协方差0.0011
    [0.0018, 0.0081, 0.0009],  # 股票B:与A协方差0.0018,方差0.0081,与C协方差0.0009
    [0.0011, 0.0009, 0.0064]   # 股票C:与A协方差0.0011,与B协方差0.0009,方差0.0064
])  # 对称矩阵,反映3只股票之间的风险关系

# ==================== 投资组合权重 ====================
weights = np.array([0.3, 0.5, 0.2])  # 定义投资组合权重:股票A 30%, B 50%, C 20%

# ==================== 方法1:矩阵运算计算组合方差(简洁高效) ====================
portfolio_var = weights @ cov_matrix @ weights.T  # 公式:wΣw',权重×协方差矩阵×权重转置
portfolio_std = np.sqrt(portfolio_var)  # sqrt开方得到标准差(波动率)

print(f"投资组合方差: {portfolio_var:.6f}")  # 打印方差
print(f"投资组合标准差(波动率): {portfolio_std:.4%}")  # 打印波动率,:.4%格式化为百分比

# ==================== 方法2:双重求和(展示数学原理) ====================
n = len(weights)  # 获取资产数量n=3
portfolio_var2 = 0  # 初始化组合方差为0
for i in range(n):  # 外层循环遍历所有资产i
    for j in range(n):  # 内层循环遍历所有资产j
        portfolio_var2 += weights[i] * weights[j] * cov_matrix[i, j]  # 累加:wi*wj*σij

print(f"\n验证-投资组合方差: {portfolio_var2:.6f}")  # 打印双重求和计算的结果
print(f"两种方法一致: {np.isclose(portfolio_var, portfolio_var2)}")  # isclose比较两个浮点数是否近似相等

# ==================== 分散化效应:组合风险 < 加权平均风险 ====================
individual_stds = np.sqrt(np.diag(cov_matrix))  # diag提取对角线元素(方差),sqrt开方得到各资产标准差
weighted_avg_std = np.sum(weights * individual_stds)  # sum计算加权平均标准差
print(f"\n加权平均标准差: {weighted_avg_std:.4%}")  # 打印加权平均标准差
print(f"组合标准差: {portfolio_std:.4%}")  # 打印组合标准差
print(f"风险降低: {(weighted_avg_std - portfolio_std) / weighted_avg_std:.2%}")  # 计算并打印风险降低比例
# 分散化效应:组合标准差 < 加权平均标准差,体现了"不要把鸡蛋放在同一个篮子里"的原理

7.5 线性方程组求解

在金融中,许多问题可以表示为线性方程组\(\mathbf{Ax} = \mathbf{b}\)

应用场景: - 因子收益计算: 从资产收益反推因子收益 - 套利定价理论: 求解无风险组合权重 - 状态估计: Kalman滤波中的状态更新

列表 7.7
# =============================================================================
# 题目:求解线性方程组在金融中的应用
# =============================================================================
# 套利定价理论(APT)场景:已知资产收益率和因子载荷,求解因子收益
# 数学形式: Ax = b,其中A是因子载荷矩阵,x是因子收益,b是资产收益

import numpy as np  # 导入NumPy库

# ==================== 数据准备:因子载荷矩阵 ====================
A = np.array([  # 定义3×3因子载荷矩阵
    [1.2, 0.8, 0.5],  # 资产1对3个因子的敏感度(贝塔值)
    [0.9, 1.1, 0.3],  # 资产2对3个因子的敏感度
    [0.7, 0.6, 0.9]   # 资产3对3个因子的敏感度
])  # A矩阵每行代表一只资产,每列代表一个因子

# ==================== 资产收益率(超收益) ====================
b = np.array([0.08, 0.06, 0.07])  # 定义3只资产的收益率向量(超收益部分)

# ==================== 求解因子收益:Ax = b ====================
factor_returns = np.linalg.solve(A, b)  # solve函数求解线性方程组,返回因子收益向量x
print("因子收益率:")
print(f"  因子1: {factor_returns[0]:.4%}")  # 打印因子1的收益率
print(f"  因子2: {factor_returns[1]:.4%}")  # 打印因子2的收益率
print(f"  因子3: {factor_returns[2]:.4%}")  # 打印因子3的收益率

# ==================== 验证解的正确性 ====================
b_reconstructed = A @ factor_returns  # 将求解的因子收益代回方程:Ax
print(f"\n重构收益率: {b_reconstructed}")  # 打印重构的资产收益
print(f"原始收益率: {b}")  # 打印原始收益
print(f"验证通过: {np.allclose(b, b_reconstructed)}")  # allclose验证重构值与原始值是否一致

7.6 矩阵的秩与逆

矩阵的秩(Rank): 矩阵的行(列)向量中线性无关的最大数目。

可逆矩阵(非奇异矩阵): 方阵\(\mathbf{A}\)可逆当且仅当: 1. \(\det(\mathbf{A}) \neq 0\) 2. \(\text{rank}(\mathbf{A}) = n\) 3. \(\mathbf{Ax} = \mathbf{0}\)只有零解

列表 7.8
# =============================================================================
# 题目:矩阵的秩与逆运算
# =============================================================================
# 可逆矩阵是线性代数的重要概念,在金融中用于求解线性方程组和计算组合权重
# 矩阵可逆的充要条件:行列式≠0 且 秩=n

import numpy as np  # 导入NumPy库

# ==================== 定义一个可逆矩阵 ====================
A = np.array([  # 定义2×2方阵
    [4, 7],  # 第1行
    [2, 6]   # 第2行
])  # 这是一个可逆矩阵

print("矩阵A:")
print(A)  # 打印矩阵A

# ==================== 计算行列式(Determinant) ====================
det_A = np.linalg.det(A)  # det函数计算行列式,可逆矩阵行列式≠0
print(f"\n行列式: {det_A:.4f}")  # 打印行列式值

# ==================== 计算矩阵的秩 ====================
rank_A = np.linalg.matrix_rank(A)  # matrix_rank计算矩阵的秩(线性无关的行/列数)
print(f"秩: {rank_A}")  # 打印秩,满秩矩阵秩等于维度

# ==================== 计算逆矩阵 ====================
A_inv = np.linalg.inv(A)  # inv函数计算逆矩阵,A^(-1)满足A×A^(-1)=I
print("\n逆矩阵 A^(-1):")
print(A_inv)  # 打印逆矩阵

# ==================== 验证: A @ A^(-1) = I (单位矩阵) ====================
identity = A @ A_inv  # 矩阵乘法:A × A^(-1)
print("\nA @ A^(-1):")
print(identity)  # 打印乘积,理论上应等于单位矩阵
print(f"是否为单位矩阵: {np.allclose(identity, np.eye(2))}")  # allclose验证是否为单位矩阵
# np.eye(2)创建2×2单位矩阵

# ==================== 求解线性方程组(使用逆矩阵方法) ====================
# 方程组: Ax = b,解为: x = A^(-1) @ b
b = np.array([1, 2])  # 定义方程组右侧向量b
x = A_inv @ b  # 利用逆矩阵求解:x = A^(-1) × b
print(f"\n方程组Ax=b的解: {x}")  # 打印解向量x

补充说明:数值稳定性

在实际计算中,接近奇异的矩阵(行列式接近0)会导致数值不稳定。NumPy会抛出LinAlgError

金融意义: 协方差矩阵接近奇异意味着存在完全相关的资产,这在实际中很少见(因为资产价格总有噪声)。

7.7 特征值与特征向量

定义: 对于方阵\(\mathbf{A}\),如果存在非零向量\(\mathbf{v}\)和标量\(\lambda\)使得:

\[ \mathbf{Av} = \lambda\mathbf{v} \]

\(\lambda\)称为特征值,\(\mathbf{v}\)称为特征向量。

金融应用: 1. 主成分分析(PCA): 降维和因子提取 2. 协方差矩阵对角化: 找到不相关的风险因子 3. 马尔可夫链: 稳态分布计算

列表 7.9
# =============================================================================
# 题目:特征值分解在主成分分析中的应用
# =============================================================================
# 特征值和特征向量是主成分分析(PCA)的数学基础
# 特征值大小代表主成分解释的方差量,特征向量代表主成分方向

import numpy as np  # 导入NumPy库

# ==================== 数据准备:协方差矩阵 ====================
cov_matrix = np.array([  # 定义3×3协方差矩阵
    [0.0100, 0.0018, 0.0011],  # 资产1的协方差
    [0.0018, 0.0081, 0.0009],  # 资产2的协方差
    [0.0011, 0.0009, 0.0064]   # 资产3的协方差
])  # 协方差矩阵是实对称矩阵,特征值均为实数

# ==================== 计算特征值和特征向量 ====================
eigenvalues, eigenvectors = np.linalg.eigh(cov_matrix)  # eigh计算对称矩阵的特征值分解
# eigh返回:特征值数组(升序排列),特征向量矩阵(每列是一个特征向量)

print("特征值:")
for i, val in enumerate(eigenvalues):  # enumerate遍历特征值索引和值
    print(f"  λ{i+1} = {val:.6f}")  # 打印每个特征值,从大到小排列代表主成分重要性

print("\n特征向量(每列是一个特征向量):")
print(eigenvectors)  # 打印特征向量矩阵,第i列对应第i个特征值

# ==================== 计算解释方差比例 ====================
explained_var_ratio = eigenvalues / eigenvalues.sum()  # 每个特征值占总和的比例
print(f"\n解释方差比例:")
for i, ratio in enumerate(explained_var_ratio):  # 遍历解释方差比例
    print(f"  PC{i+1}: {ratio:.2%}")  # 打印每个主成分解释的方差比例

# ==================== 计算累计解释方差 ====================
cumulative_var = np.cumsum(explained_var_ratio)  # cumsum计算累积和
print(f"\n累计解释方差:")
for i, cum_var in enumerate(cumulative_var):  # 遍历累积方差
    print(f"  前{i+1}个主成分: {cum_var:.2%}")  # 打印前k个主成分的累计解释方差比例

7.8 广义逆矩阵

对于非方阵或奇异矩阵,可以使用Moore-Penrose伪逆。

列表 7.10
# =============================================================================
# 题目:伪逆在超定系统中的应用
# =============================================================================
# 伪逆(广义逆)用于处理非方阵或奇异矩阵的求逆问题
# 在最小二乘回归中,伪逆提供最优解:x = A^+ × b

import numpy as np  # 导入NumPy库

# ==================== 定义超定系统(方程数>未知数) ====================
A = np.array([  # 定义4×2矩阵,4个方程2个未知数
    [1, 2],  # 第1行
    [3, 4],  # 第2行
    [5, 6],  # 第3行
    [7, 8]   # 第4行
])  # 这是一个超定系统,通常无精确解,只能求最小二乘解

b = np.array([1, 2, 3, 4])  # 定义右侧向量b,4个元素

# ==================== 计算Moore-Penrose伪逆 ====================
A_pinv = np.linalg.pinv(A)  # pinv计算伪逆(Moore-Penrose广义逆)

# ==================== 求解最小二乘解 ====================
x = A_pinv @ b  # 伪逆 × b,得到最小二乘解,最小化||Ax - b||²
print(f"最小二乘解: {x}")  # 打印解向量

# ==================== 验证:计算残差和均方误差 ====================
residual = b - (A @ x)  # 计算残差向量:b - Ax
mse = np.mean(residual ** 2)  # mean计算残差平方的均值(均方误差)
print(f"均方误差: {mse:.6f}")  # 打印均方误差,值越小拟合越好